package com.atguigu.lease.web.app.service.impl;

import com.atguigu.lease.common.constant.RedisConstant;
import com.atguigu.lease.common.exception.LeaseException;
import com.atguigu.lease.common.result.ResultCodeEnum;
import com.atguigu.lease.common.utils.JwtUtil;
import com.atguigu.lease.common.utils.VerifyCodeUtil;
import com.atguigu.lease.model.entity.UserInfo;
import com.atguigu.lease.model.enums.BaseStatus;
import com.atguigu.lease.web.app.service.LoginService;
import com.atguigu.lease.web.app.service.SmsService;
import com.atguigu.lease.web.app.service.UserInfoService;
import com.atguigu.lease.web.app.vo.user.LoginVo;
import com.atguigu.lease.web.app.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;

/**
 * Package: com.atguigu.lease.web.app.service.impl
 *
 * @Description:
 * @Author xiongkunYang
 * @Create 2025/4/28 13:55
 */
@RequiredArgsConstructor
@Service
public class LoginServiceImpl implements LoginService {

    private final StringRedisTemplate redisTemplate;
    private final SmsService smsService;
    private final UserInfoService userInfoService;


    /**
     * 获取短信验证码
     *
     * @param phone 手机号码，用于接收验证码
     * @throws LeaseException 当手机号码为空或发送验证码过于频繁时抛出异常
     */
    @Override
    public void getSmsCode(String phone) {
        // 1 检查手机号码是否为空
        if (!StringUtils.hasText(phone)) {
            throw new LeaseException(ResultCodeEnum.APP_LOGIN_PHONE_EMPTY);
        }
        // 2 检查Redis中是否已经存在该手机号码的验证码
        String key = RedisConstant.APP_LOGIN_PREFIX + phone;
        if (this.redisTemplate.hasKey(key)) {
            Long expire = this.redisTemplate.getExpire(key);
            // 如果验证码存在且距离过期时间小于设定的重新发送间隔，则抛出异常
            if (RedisConstant.APP_LOGIN_CODE_TTL_SEC - expire < RedisConstant.APP_LOGIN_CODE_RESEND_TIME_SEC) {
                throw new LeaseException(ResultCodeEnum.APP_SEND_SMS_TOO_OFTEN);
            }
        }
        // 3 发送短信验证码，将验证码保存到Redis中
        String verifyCode = VerifyCodeUtil.getVerifyCode(4);
        this.smsService.sendCode(phone, verifyCode);
        this.redisTemplate.opsForValue()
                .set(key, verifyCode, RedisConstant.APP_LOGIN_CODE_TTL_SEC, TimeUnit.SECONDS);
    }

    /**
     * 用户登录方法，验证用户输入的手机号和验证码，并生成JWT令牌。
     *
     * @param loginVo 包含用户登录信息的对象，包括手机号和验证码。
     *                - phone: 用户的手机号，不能为空。
     *                - code: 用户输入的验证码，不能为空。
     * @return 返回生成的JWT令牌，用于后续的身份验证。
     * @throws LeaseException 如果手机号或验证码为空、验证码过期、验证码错误、账户被禁用等情况，会抛出异常。
     */
    @Override
    public String login(LoginVo loginVo) {
        // 验证手机号是否为空
        String phone = loginVo.getPhone();
        if (!StringUtils.hasText(phone)) {
            throw new LeaseException(ResultCodeEnum.APP_LOGIN_PHONE_EMPTY);
        }
        // 验证验证码是否为空
        String loginCode = loginVo.getCode();
        if (!StringUtils.hasText(loginCode)) {
            throw new LeaseException(ResultCodeEnum.APP_LOGIN_CODE_EMPTY);
        }
        // 从Redis中获取存储的验证码并验证其有效性
        String key = RedisConstant.APP_LOGIN_PREFIX + loginVo.getPhone();
        String code = this.redisTemplate.opsForValue().get(key);
        if (code == null) {
            throw new LeaseException(ResultCodeEnum.APP_LOGIN_CODE_EXPIRED);
        }
        if (!code.equalsIgnoreCase(loginCode)) {
            throw new LeaseException(ResultCodeEnum.APP_LOGIN_CODE_ERROR);
        }
        // 根据手机号查询用户信息，如果用户不存在则创建新用户
        UserInfo userInfo = this.userInfoService
                .getOne(new LambdaQueryWrapper<UserInfo>()
                        .eq(UserInfo::getPhone, phone));
        if (userInfo == null) {
            userInfo = UserInfo.builder()
                    .phone(phone)
                    .status(BaseStatus.ENABLE)
                    .nickname("用户-" + phone.substring(5))
                    .build();
            this.userInfoService.save(userInfo);
        }
        // 检查用户账户状态，如果账户被禁用则抛出异常
        if (userInfo.getStatus() == BaseStatus.DISABLE) {
            throw new LeaseException(ResultCodeEnum.APP_ACCOUNT_DISABLED_ERROR);
        }
        // 生成并返回JWT令牌
        return JwtUtil.createToken(userInfo.getId(), userInfo.getPhone());
    }

    @Override
    public UserInfoVo getUserInfoByUserId(Long userId) {
        UserInfo userInfo = this.userInfoService.getById(userId);
        return new UserInfoVo(userInfo.getNickname(), userInfo.getAvatarUrl());
    }
}